// #include <gl\glext.h>
#include "critterb.h"

void CritterB::initConst()
{
	// settings and pointers
	settings = Settings::Instance();
	genotypes = Genotypes::Instance();
	colormode = settings->getCVarPtr("colormode");
	critter_maxlifetime = settings->getCVarPtr("critter_maxlifetime");
	critter_maxenergy = settings->getCVarPtr("critter_maxenergy");
	critter_sightrange = settings->getCVarPtr("critter_sightrange");
	critter_procinterval = settings->getCVarPtr("critter_procinterval");
	critter_minenergyproc = settings->getCVarPtr("critter_minenergyproc");

	brain_costhavingneuron = settings->getCVarPtr("brain_costhavingneuron");
	brain_costfiringneuron = settings->getCVarPtr("brain_costfiringneuron");
	brain_costfiringmotorneuron = settings->getCVarPtr("brain_costfiringmotorneuron");
	brain_costhavingsynapse = settings->getCVarPtr("brain_costhavingsynapse");


	totalFrames		= 0;
	procreateTimeCount	= 0;

// 	sightrange = settings->critter_sightrange;
// 	colorTrim		= 0.15f;

	procreate		= false;
  deathreason = "starvation";
  beingEaten = false;
  beingTouched = false;

}

CritterB::CritterB(btDynamicsWorld* btWorld, long unsigned int id, const btVector3& startPos)
{
	loadError = false;

	// first things first
	btDynWorld						= btWorld;
	critterID						= id;

	initConst();

	energyLevel						= settings->getCVar("critter_startenergy");

// 	lifetime						= settings->critter_maxlifetime;

	genotype = genotypes->newg( settings );

	// BODY
// 	genotype->bodyArch->buildArch();
	body.wireArch( genotype->bodyArch, this, btDynWorld, startPos, genotype->bodyArch->color, genotype->speciescolor );

	// LINK
	registerBrainInputOutputs();
	
	// BRAIN
// 	genotype->brainzArch->buildArch();
	brain.wireArch( genotype->brainzArch );
}

CritterB::CritterB(CritterB& other, long unsigned int id, const btVector3& startPos, bool brainmutant, bool bodymutant)
{
	loadError = false;

	// first things first
	btDynWorld					= other.btDynWorld;
	critterID					= id;

	initConst();

// 	lifetime					= other.lifetime;

	genotype = genotypes->copy(other.genotype, brainmutant, BeRand::Instance()->get(1, settings->getCVar("brain_maxmutations")), bodymutant, BeRand::Instance()->get(1, settings->getCVar("body_maxmutations")));

	body.wireArch( genotype->bodyArch, this, btDynWorld, startPos, genotype->bodyArch->color, genotype->speciescolor );

	// LINK
	registerBrainInputOutputs();

	brain.wireArch( genotype->brainzArch );
}

CritterB::CritterB(string &critterstring, btDynamicsWorld* btWorld, const btVector3& startPos)
{
	loadError = false;
	// critterID is arranged in world, definite critter insertion is not determined yet

	// first things first
	btDynWorld		= btWorld;

	initConst();

	energyLevel		= settings->getCVar("critter_startenergy");

	genotype = genotypes->loadGenotype(critterstring);

	if ( genotype->bodyArch->retinasize != settings->getCVar("critter_retinasize"))
	{
		stringstream buf;
		buf << "ERROR: critter retinasize (" << genotype->bodyArch->retinasize << ") doesn't fit world retinasize (" << settings->getCVar("critter_retinasize") << ")";
		Logbuffer::Instance()->add(buf);

		cerr << "!ERROR: critter retinasize (" << genotype->bodyArch->retinasize << ") doesn't fit world retinasize (" << settings->getCVar("critter_retinasize") << ")" << endl;
		loadError = true;
	}
	else
	{
		// BODY
		body.wireArch( genotype->bodyArch, this, btDynWorld, startPos, genotype->bodyArch->color, genotype->speciescolor );

		// LINK
		registerBrainInputOutputs();

		// BRAIN
		brain.wireArch( genotype->brainzArch );
	}
}

void CritterB::registerBrainInputOutputs()
{
  int nindex = 1;
 	//cerr << "attaching INPUTS " << nindex << endl;
	for ( unsigned int i=0; i < genotype->brainzArch->InputIDs.size(); i++ )
	{
		brain.registerInput( nindex++ );
	}
 	//cerr << "attached INPUTS " << nindex << endl;

	// debug check
	if ( brain.Inputs.size() != genotype->brainzArch->InputIDs.size() )
		cerr << "WARNING: brain.Inputs.size() != genotype->brainzArch->InputIDs.size()" << endl;

  // 	cerr << "attaching OUTPUTS" << endl;

  nindex = 1;

	for ( unsigned int i=0; i < genotype->brainzArch->OutputIDs.size(); i++ )
	{
		brain.registerOutput( nindex++ );
	}
		
	// debug check
	if ( brain.Outputs.size() != genotype->brainzArch->OutputIDs.size() )
		cerr << "WARNING: brain.Outputs.size() != genotype->brainzArch->OutputIDs.size()" << endl;
}

void CritterB::draw()
{
	for( unsigned int j=0; j < body.bodyparts.size(); j++)
	{
    body.bodyparts[j]->draw(*colormode);
	}
}


void CritterB::preprocess(vector<CritterB*>& critters, vector<Entity*>& entities, unsigned long long frameCount)
{
  for(int i=0;i < body.bodyparts.size(); i++)
  {
    body.bodyparts[i]->preprocess(critters, entities, frameCount);
  }
}


void CritterB::process(vector<CritterB*>& critters, vector<Entity*>& entities, unsigned long long frameCount)
{
    // So as not to confuse, 'totalFrames' is lifetime of this creature, 'frameCount' is total world frames 'till now.
	// increase counters
		totalFrames++;
		procreateTimeCount++;

	// reset motor bools
		movementsmade	= 0;
 		procreate	= false;
// 		carrydrop	= false;

  // Bodyparts
  for(int i=0;i < body.bodyparts.size(); i++)
    body.bodyparts[i]->process(critters, entities, frameCount);

	// SENSORS
		procInputNeurons();

	// INTERS
		brain.process();

	// MOTORS
 		procOutputNeurons();

	// calc used energy, energyUsed is used in world aswell, don't remove

// 		energyUsed = ( (float)brain.totalNeurons + (float)brain.neuronsFired + (2.0f*(float)motorneuronsfired) + ((float)brain.totalSynapses/10.0f) ) / 200.0f;

 		energyUsed =  brain.totalNeurons	* *brain_costhavingneuron;
 		energyUsed += brain.neuronsFired	* *brain_costfiringneuron;
 		energyUsed += brain.motorneuronsFired	* *brain_costfiringmotorneuron;
 		energyUsed += brain.totalSynapses	* *brain_costhavingsynapse;
		energyUsed = energyUsed / 100000;
// 		cerr << energyUsed << endl;

	// apply energy usage
	energyLevel -= energyUsed;

	// move
		// motorate all constraints
		for ( unsigned int i=0; i < body.constraints.size(); i++ )
			body.constraints[i]->motorate();
}

void CritterB::postprocess(vector<CritterB*>& critters, vector<Entity*>& entities, unsigned long long frameCount)
{
  for(int i=0;i < body.bodyparts.size(); i++)
    body.bodyparts[i]->postprocess(critters, entities, frameCount);

  // Die of old age?
  if(totalFrames >= *critter_maxlifetime)
  {
    energyLevel = 0.0f;
    deathreason = "old age";
  }

  beingTouched = false;
  beingEaten   = false;

}



void CritterB::procInputNeurons()
{
	// clear all inputs

	brain.clearInputs();

	unsigned int overstep = 0;

	// can procreate sensor neuron
		canProcreate	= false;
		if ( procreateTimeCount > *critter_procinterval && energyLevel > *critter_minenergyproc )
		{
			brain.Inputs[overstep++].output = 1;
			canProcreate = true;
		}
		else brain.Inputs[overstep++].output = 0;

	// being touched
		if ( beingTouched )	brain.Inputs[overstep++].output = 1;
		else			brain.Inputs[overstep++].output = 0;

	// being eaten
		if ( beingEaten )	brain.Inputs[overstep++].output = 1;
		else			brain.Inputs[overstep++].output = 0;

	// energy neurons
		unsigned int NeuronToFire = (int)((energyLevel / (*critter_maxenergy+1)) * 10) + overstep;
		unsigned int count = 10 + overstep;
		while ( overstep < count )
		{
			if ( overstep <= NeuronToFire )	brain.Inputs[overstep++].output = 1; // !!! <=
			else 				brain.Inputs[overstep++].output = 0;
		}

	// age neurons
		NeuronToFire = (int)(((float)totalFrames / (*critter_maxlifetime+1)) * 10) + overstep;
		count = 10 + overstep;
		while ( overstep < count )
		{
			if ( overstep <= NeuronToFire )	brain.Inputs[overstep++].output = 1; // !!! <=
			else 				brain.Inputs[overstep++].output = 0;
		}

	// constraint angle neurons
		for ( unsigned int i=0; i < body.constraints.size(); i++ )
      overstep = body.constraints[i]->procInputs(brain, overstep);

	// Vision + bodypart specific

    for(unsigned int i=0;i < body.bodyparts.size(); i++)
    {
      overstep = body.bodyparts[i]->procInputs(brain, overstep);
    }


	// debugging check
		if ( overstep != brain.numberOfInputs )
		{
			cerr << "procInputNeurons " << overstep << " does not equal " << brain.numberOfInputs << endl;
			exit(0);
		}
}

unsigned int CritterB::getNumberOfInputs()
{
  return 1 + 1 + 1 + 10 + 10;
}



void CritterB::procOutputNeurons()
{
	unsigned int overstep = 0;

  //cerr << brain.Outputs[overstep].output << ":";
  procreate = false;
  if(brain.Outputs[overstep++].output != 0)
    procreate = true;

  //cerr << "procOutputNeurons: ";
  for ( unsigned int i=0; i < body.constraints.size(); i++ )
  {
    overstep = body.constraints[i]->procOutputs(brain, overstep);
  }

  for(unsigned int i=0;i < body.bodyparts.size(); i++)
  {
    overstep = body.bodyparts[i]->procOutputs(brain, overstep);
  }

  //cerr << endl;

  if ( overstep != brain.numberOfOutputs )
	{
		cerr << "procOutputNeurons " << overstep << " does not equal " << brain.numberOfOutputs << endl;
	}
}

unsigned int CritterB::getNumberOfOutputs()
{
  return 1;
}


float CritterB::beEaten(float amount)
{
  if(! *(Settings::Instance()->getCVarPtr("critter_enableomnivores")))
    return 0;
  return Bodyowner::beEaten(amount);
}



CritterB::~CritterB()
{
	genotypes->remove(genotype);
}


